/*=========================================================================

     morder.cpp  -  Copyright (C) 1993-2005 by Don Cross

     Contains move-ordering heuristics for the min-max search.

=========================================================================*/

#include "chess.h"
#include "profiler.h"

#define  PREV_SQUARE_BONUS      (gene.v[73])
#define  CHECK_BONUS            (gene.v[74])
#define  KILLER_MOVE_BONUS      (gene.v[75])
#define  HASH_HIST_SHIFT        (gene.v[76])
#define  PAWN_CAPTURE_PENALTY   (gene.v[77])
#define  PAWN_DANGER_PENALTY    (gene.v[78])
#define  FORWARD_BONUS          (gene.v[79])
#define  CASTLE_BONUS           (gene.v[80])

#define  WHITE_BEST_PATH     (20000)
#define  BLACK_BEST_PATH    (-20000)

#define  WHITE_XPOS       (10000)
#define  BLACK_XPOS      (-10000)


#ifdef __BORLANDC__
    #pragma argsused
#endif

void ComputerChessPlayer::WhiteMoveOrdering ( 
    const ChessBoard &board,
    Move &move,
    const UnmoveInfo &unmove,
    int depth,
    bool bestPathFlag )
{
#ifndef NO_SEARCH
    PROFILER_ENTER(PX_MOVEORDER)

    // First, check for best path.  It overrides everything!

    if ( bestPathFlag &&
        depth <= currentBestPath.depth &&
        depth < MAX_BESTPATH_DEPTH &&
        currentBestPath.m[depth] == move )
    {
        move.score = WHITE_BEST_PATH;
        return;
    }

    if ( move == moveOrder_xposBestMove )
    {
        move.score = WHITE_XPOS;
        return;
    }

    move.score = MaterialEval ( board.wmaterial, board.bmaterial );

    const int source = move.source & BOARD_OFFSET_MASK;

    // "killer move" heuristic...

    if ( depth > 0 && depth < MAX_BESTPATH_DEPTH &&
         depth <= nextBestPath[depth-1].depth &&
         nextBestPath[depth-1].m[depth] == move )
        move.score += KILLER_MOVE_BONUS;

    if ( board.flags & SF_BCHECK )
        move.score += CHECK_BONUS;

    if ( move.dest == unmove.prev_move.dest )
        move.score += PREV_SQUARE_BONUS;

    if ( move.dest <= OFFSET(9,9) )
    {
        SQUARE piece = board.board [move.dest];
        move.score -= UPIECE_INDEX(piece);

        if ( piece & WP_MASK )
        {
            const int delta = move.dest - source;
            if ( delta==NORTHEAST || delta==NORTHWEST )
                move.score -= PAWN_CAPTURE_PENALTY;
        }
        else
        {
            const SQUARE *p = & board.board [move.dest];
            if ( (p[NORTHEAST] | p[NORTHWEST]) & BP_MASK )
                move.score -= PAWN_DANGER_PENALTY;
        }

        if ( source <= OFFSET(9,4) && move.dest >= source+10 )
            move.score += FORWARD_BONUS;
    }
    else
    {
        int special = move.dest & SPECIAL_MOVE_MASK;
        if (   special == SPECIAL_MOVE_KCASTLE 
            || special == SPECIAL_MOVE_QCASTLE )
        {
            move.score += CASTLE_BONUS;
        }
    }

    move.score += whiteHist [ move.whiteHash() ] >> HASH_HIST_SHIFT;

    PROFILER_EXIT();
#endif
}


#ifdef __BORLANDC__
    #pragma argsused
#endif

void ComputerChessPlayer::BlackMoveOrdering ( 
    const ChessBoard &board,
    Move &move,
    const UnmoveInfo &unmove,
    int depth,
    bool bestPathFlag )
{
#ifndef NO_SEARCH
    PROFILER_ENTER(PX_MOVEORDER);

    // First, check for best path.  It overrides everything!

    if ( bestPathFlag &&
        depth <= currentBestPath.depth &&
        depth < MAX_BESTPATH_DEPTH &&
        currentBestPath.m[depth] == move )
    {
        move.score = BLACK_BEST_PATH;
        return;
    }

    if ( move == moveOrder_xposBestMove )
    {
        move.score = BLACK_XPOS;
        return;
    }

    move.score = MaterialEval ( board.wmaterial, board.bmaterial );


    const int source = move.source & BOARD_OFFSET_MASK;

    // "killer move" heuristic...

    if ( depth > 0 && depth < MAX_BESTPATH_DEPTH &&
         depth <= nextBestPath[depth-1].depth &&
         nextBestPath[depth-1].m[depth] == move )
        move.score -= KILLER_MOVE_BONUS;

    if ( board.flags & SF_WCHECK )
        move.score -= CHECK_BONUS;

    if ( move.dest == unmove.prev_move.dest )
        move.score -= PREV_SQUARE_BONUS;

    if ( move.dest <= OFFSET(9,9) )
    {
        const SQUARE piece = board.board [move.dest];
        move.score += UPIECE_INDEX(piece);
        if ( piece & BP_MASK )
        {
            const int delta = move.dest - source;
            if ( delta==SOUTHEAST || delta==SOUTHWEST )
                move.score += PAWN_CAPTURE_PENALTY;
        }
        else
        {
            const SQUARE *p = & board.board [move.dest];
            if ( (p[SOUTHEAST] | p[SOUTHWEST]) & WP_MASK )
                move.score += PAWN_DANGER_PENALTY;
        }

        if ( source >= OFFSET(2,7) && move.dest <= source-10 )
            move.score -= FORWARD_BONUS;
    }
    else
    {
        int special = move.dest & SPECIAL_MOVE_MASK;
        if (   special == SPECIAL_MOVE_KCASTLE 
            || special == SPECIAL_MOVE_QCASTLE )
        {
            move.score -= CASTLE_BONUS;
        }
    }

    move.score -= blackHist [ move.blackHash() ] >> HASH_HIST_SHIFT;

    PROFILER_EXIT();
#endif
}


//----------------------------------------------------------------------

void MoveList::WhiteSort()
{
#ifndef NO_SEARCH
    PROFILER_ENTER(PX_MOVEORDER);

    if ( num > 1 )
    {
        SCORE bestscore;
        int   besti, i;
        int   limit = num - 1;

        for ( int dest=0; dest < limit; dest++ )
        {
            bestscore = m [besti = dest].score;
            for ( i=dest + 1; i < num; i++ )
            {
                if ( m[i].score > bestscore )
                {
                    bestscore = m[i].score;
                    besti = i;
                }
            }

            if ( besti != dest )
            {
                Move t = m[dest];
                m[dest] = m[besti];
                m[besti] = t;
            }
        }
    }

    PROFILER_EXIT()
#endif
}


void MoveList::BlackSort()
{
#ifndef NO_SEARCH
   PROFILER_ENTER(PX_MOVEORDER)

    if ( num > 1 )
    {
        SCORE bestscore;
        int   besti, i;
        int   limit = num - 1;

        for ( int dest=0; dest < limit; dest++ )
        {
            bestscore = m [besti = dest].score;
            for ( i=dest + 1; i < num; i++ )
            {
                if ( m[i].score < bestscore )
                {
                    bestscore = m[i].score;
                    besti = i;
                }
            }

            if ( besti != dest )
            {
                Move t = m[dest];
                m[dest] = m[besti];
                m[besti] = t;
            }
        }
    }

   PROFILER_EXIT();
#endif
}


void MoveList::Shuffle()
{
    for (int i=1; i < num; ++i) {
        int r = ChessRandom (i+1);
        if (r < i) {
            Move t = m[r];
            m[r]   = m[i];
            m[i]   = t;
        }
    }
}
